/////////////////////////////////////////////////
///file: card_4200
///Author: Qyw
///card_beh file
////////////////////////////////////////////////////

`define SEED 10;

module card_4200(//input
				clk,
				mod,
				error,
				//output
				manc,
				ch
				);

parameter	PWIDTH = 32;
parameter	PWIDTH_BP = 16;

input			clk,
				mod; // 1 is 128bits , 0 is 64bits
input	[1:0]	error; // {p_err, data_err}
input	[2:0]	ch;
output			manc;

reg				manc;
reg		[7:0]	mem[15:0];
reg		[31:0]	do_cnt;

event 			changed;

/*****************bp_seq1*********************/
task bp_seq1;
begin
	manc = ~manc;
	repeat(PWIDTH_BP * 2) @(posedge clk);
end
endtask

/*****************bp_seq0*********************/
task bp_seq0;
begin
	manc = ~manc;
	repeat(PWIDTH_BP) @(posedge clk);
	manc = ~manc;
	repeat(PWIDTH_BP) @(posedge clk);
end
endtask

/*****************seq1************************/
task seq1;
begin
	manc = 1'b0;
	repeat(PWIDTH)@(posedge clk);
	manc = 1'b1;
	repeat(PWIDTH)@(posedge clk);
end
endtask

/**************seq0****************************/
task seq0;
begin
	manc = 1'b1;
	repeat(PWIDTH)@(posedge clk);
	manc = 1'b0;
	repeat(PWIDTH)@(posedge clk);
end
endtask

/**************coder one byte*********************/
task coder_one_byte;
input	[7:0]	byte;
begin: cob
	integer i;
	for(i = 0; i < 8; i = i+1)
	begin
		if(byte[7-i])
		begin
			if(mod)
				bp_seq1;
			else
				seq1;
		end
		else
		begin
			if(mod)
				bp_seq0;
			else
				seq0;
		end
	end
end
endtask

/******************write_mem*************************************/
task wirte_mem;
input [127:0] buff;
integer f, i;
begin
	if(mod)
	begin
		case(ch)
		3'b000:f = $fopen("128bits_ch0.out");
		3'b001:f = $fopen("128bits_ch1.out");
		3'b010:f = $fopen("128bits_ch2.out");
		3'b011:f = $fopen("128bits_ch3.out");
		3'b100:f = $fopen("128bits_ch4.out");
		3'b101:f = $fopen("128bits_ch5.out");
		3'b110:f = $fopen("128bits_ch6.out");
		3'b111:f = $fopen("128bits_ch7.out");
		endcase
		$fdisplay(f, "%11b", buff[127:117]);
		for(i = 0; i < 13; i = i+1)
			$fdisplay(f, "%8b|%1b", buff[(116-9*i)-:8],buff[108-9*i]);	
	end		
	else
	begin
		case(ch)
		3'b000:f = $fopen("64bits_ch0.out");
		3'b001:f = $fopen("64bits_ch1.out");
		3'b010:f = $fopen("64bits_ch2.out");
		3'b011:f = $fopen("64bits_ch3.out");
		3'b100:f = $fopen("64bits_ch4.out");
		3'b101:f = $fopen("64bits_ch5.out");
		3'b110:f = $fopen("64bits_ch6.out");
		3'b111:f = $fopen("64bits_ch7.out");
		endcase
		$fdisplay(f, "%8b|%1b", buff[63:56], buff[55]);
		$fdisplay(f, "    %4b|%1b", buff[54:51], buff[50]);
		$fdisplay(f, "    %4b|%1b", buff[49:46], buff[45]);
		$fdisplay(f, "--------------------");
		for(i = 0; i < 9; i = i+1)
		begin
			$fdisplay(f, "    %4b|%1b", buff[44-i*5-:4], buff[40-i*5]);	
		end
	end
	$fclose(f);
end
endtask

/********************calc_crc************************************/
task calc_crc;
input	[7:0]din;
input	[15:0]tmp;
output	[15:0]crc;
integer i;
begin
	for(i = 0; i < 8; i = i+1)
	begin		
		if(din[7-i] ^ tmp[15])
		begin	
			tmp = tmp << 1;
			tmp = tmp ^ 16'h1021;
		end
		else
			tmp = tmp << 1;
	end
	crc = tmp;
end
endtask

/********************mem init************************************/
task mem_init;
input	trans_err;			//parity error or crc error, 1 is error
input	data_err;		//the vendor data error, 1 is error
begin: mit
	reg[63:0]	uid;
	reg[12:0]	parity;
	reg[15:0]	tmp,
				crc;
	reg[63:0]	prefix;
	reg[127:0]	buff;
	reg[7:0]	tmp0;
	integer i;
	
	manc = 0;
	repeat(10)@(posedge clk);
	if(mod)		//128 bits mod
	begin: bits128
		crc = 16'hffff;
		prefix[10:0] = 11'b000_0000_0001;
		if(data_err)
			uid = 64'd0;	
		else
			uid = {
					38'b1111_1111_1111_1101_1111_1011_1001_1110_1101_11,	
					10'b11_0111_1110,
					1'b1,
					14'b0000_0000_0000_11,
					1'b0
					};
		if(~trans_err)
		begin
			crc = 16'h0000;
			tmp = crc;
			for(i = 0; i < 8; i = i+1)
			begin
				tmp0 = uid[(63-8*i)-:8];
				//$display("data to crc is: %4h", tmp0[7:0]);
				calc_crc(tmp0[7:0], tmp, crc);
				tmp = crc;
			end
			//$display("crc res is %8h", crc); 
		end
		if(data_err)
			crc = 16'hffff;
		buff = {
				prefix[10:0],
				uid[63:56], 1'b1,
				uid[55:48], 1'b1,
				uid[47:40], 1'b1,
				uid[39:32], 1'b1,
				uid[31:24], 1'b1,
				uid[23:16], 1'b1,
				uid[15:8], 1'b1,
				uid[7:0], 1'b1,
				crc[15:8], 1'b1,
				crc[7:0],1'b1,
				9'b0000_0000_1,
				9'b0000_0000_1,
				9'b0000_0000_1
					};
		for(i = 0; i < 16; i= i+1)
			mem[i] = buff[(127-8*i)-:8];
		wirte_mem(buff);
	end
	else
	begin: bits64				//64bits mod
		uid[31:0] = {$random};
		if(data_err)
			prefix[18:0] = 19'b111_1111_1100_0000_0000;
		else
			prefix[18:0] = 19'b111_1111_1111_1011_0010;
		for(i = 0; i < 8 ; i = i+1)
		begin
			parity[12-i] = ^{uid[31-i*4], uid[30-i*4], uid[29-i*4], uid[28-i*4]};
			if(trans_err)
				parity[12-i] = ~parity[12-i];
			if(i < 4)
			begin
				parity[4-i] = ^{prefix[9-i], prefix[4-i], uid[31-i], uid[27-i], uid[23-i], uid[19-i], uid[15-i], uid[11-i], uid[7-i], uid[3-i]};
				if(trans_err)
					parity[4-i] = ~parity[4-i];
			end
		end
		parity[0] = trans_err? 1'b1: 1'b0;
		buff[63:0] = {
				prefix[18:10], //9 fix
				prefix[9:0], //vender data;
				uid[31:28], parity[12],
				uid[27:24], parity[11],
				uid[23:20], parity[10],
				uid[19:16], parity[9],
				uid[15:12], parity[8],
				uid[11:8], parity[7],
				uid[7:4], parity[6],
				uid[3:0], parity[5],
				parity[4:0]
				};
   		for(i = 0; i < 8; i = i + 1)
		begin
			mem[i] = buff[(63-8*i) -:8];
		end
		wirte_mem(buff);
	end
end
endtask

/**********************send********************************************/
task send;
integer i;
integer length;
begin: send_data
	length = mod ? 16: 8;
	//$display("sending data");
	for(i = 0; i < length; i = i+1)
		coder_one_byte(mem[i]);
end
endtask	

initial
begin
	manc = 0;
	repeat(10)@(posedge clk);
	forever
	begin
		mem_init(error[1], error[0]);
		repeat(10)@(posedge clk);
		//$display("begin to send");
		begin: always_send
			forever
			fork: main_send
				begin
					send;
					disable main_send;
				end
				begin
					@(changed)
					begin
						disable always_send;
					end
				end
			join
		end
	end
end

always @(mod or error)
	->changed;

endmodule
